Skip to content

Conversation

@jhawthorn
Copy link
Contributor

@jhawthorn jhawthorn commented Jul 6, 2019

In 0371ed4 from the discussions in #576, we switched the default method of detecting processor count from /usr/bin/nproc to reading /proc/cpuinfo. This avoids a fork, but means that we no longer respect CPU affinity, which we should want if using this value to control parallelism. I imagine it should work on Alpha too.

MRI has had Etc.nprocessors since Ruby 2.2, which uses:

  • sched_getaffinity(): Linux
  • sysconf(_SC_NPROCESSORS_ONLN): GNU/Linux, NetBSD, FreeBSD, OpenBSD, DragonFly BSD, OpenIndiana, Mac OS X, AIX
  • GetSystemInfo(): Win32

This should be faster than the alternatives of reading from /proc/cpuinfo, using WIN32OLE, or spawning a new process (though all should be plenty fast since this is memoized). It also will take into account CPU affinity.

Before:

$ taskset 0x3 ruby -Ilib -rconcurrent-ruby -e 'puts Concurrent.processor_count'
12
$ be ruby -Ilib benchmark_processor_count.rb
compute_processor_count
                          5.581k (± 0.9%) i/s -     27.999k in   5.017628s

After:

$ taskset 0x3 bundle exec ruby -Ilib -rconcurrent-ruby -e 'puts Concurrent.processor_count'
2
$ be ruby -Ilib benchmark_processor_count.rb
compute_processor_count
                          1.685M (± 1.0%) i/s -      8.442M in   5.010886s

cc @pitr-ch @matthewd @graaff

MRI has had Etc.nprocessors since Ruby 2.2, which uses:
- sched_getaffinity(): Linux
- sysconf(_SC_NPROCESSORS_ONLN): GNU/Linux, NetBSD, FreeBSD, OpenBSD, DragonFly BSD, OpenIndiana, Mac OS X, AIX
- GetSystemInfo(): Win32

This should be faster than the alternatives of reading from
/proc/cpuinfo, using WIN32OLE, or spawning a new process.

This also has the advantage of reporting processor count based on CPU
affinity which /usr/bin/nproc did, but reading /proc/cpuinfo does not.
When using this value for controlling the level of parallelism, we
should ideally be using the processes CPU affinity.
@graaff
Copy link
Contributor

graaff commented Jul 7, 2019

$ ruby -r rubygems -r etc -e 'puts Etc.nprocessors'
4

(4 is the correct answer for this machine)

$ ruby -v
ruby 2.4.6p354 (2019-04-01 revision 67394) [alpha-linux]

$ uname -a
Linux monolith 4.19.37+ #44 SMP Wed Jun 19 13:07:21 CEST 2019 alpha EV68AL Tsunami GNU/Linux

def compute_processor_count
if Concurrent.on_jruby?
java.lang.Runtime.getRuntime.availableProcessors
elsif defined?(Etc) && Etc.respond_to?(:nprocessors) && (nprocessor = Etc.nprocessors rescue nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Etc is not included by default, so should this probe also include a require "etc"?

Copy link
Contributor Author

@jhawthorn jhawthorn Jul 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Good catch. I added it as a top-level require (I believe all supported rubies should support etc, older ones just don't implement nprocessors).

Copy link
Member

@eregon eregon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great to me.

@pitr-ch pitr-ch added the enhancement Adding features, adding tests, improving documentation. label Aug 24, 2019
Copy link
Member

@pitr-ch pitr-ch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@pitr-ch pitr-ch merged commit 37286c5 into ruby-concurrency:master Aug 24, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Adding features, adding tests, improving documentation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants